/*  -*- mode: c++; coding: utf-8; c-file-style: "stroustrup"; -*-

    Copyright 2008 Asier Aguirre <asier.aguirre@gmail.com>
    This file is part of Intromit.

    Intromit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Intromit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Intromit.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "render_obj_widget_container.hh"
#include "render_params.hh"
#include "render_obj_camera.hh"
#include "event_mng.hh"
#include "ogl.hh"
#include "ogl_font.hh"
#include "time_queue.hh"
#include <math.h>

// DEFINES

CLASS_INIT(render_obj_widget_container); // RENDER_OBJ_WIDGET_CONTAINER CLASS

render_obj_widget_container::render_obj_widget_container()
{
    CLASS_CTOR(render_obj_widget_container);
    ctor();
}

render_obj_widget_container::~render_obj_widget_container()
{
    CLASS_DTOR(render_obj_widget_container);
    dtor();
}

void render_obj_widget_container::ctor()
{
    grid_layout[IS_X]=5;
    grid_layout[IS_Y]=10;
    cursor_position[IS_X]=cursor_position[IS_Y]=0;
    cursor_position_bias[IS_X]=cursor_position_bias[IS_Y]=0;
    depth_pre=2;
    depth_zoom=8;
    zoom_last=-1;
}

void render_obj_widget_container::dtor()
{
}

bool render_obj_widget_container::init(render_mng *ctx)
{
    return render_obj_widget::init(ctx);
}

void render_obj_widget_container::render(render_mng *ctx)
{
    render_init(ctx);
    render_base(ctx);
    if(root->get_infront()>0 && root->get_front_blend()>=0)
    {
	if(ctx->get_pass()==RENDER_PASS_OPAQUE)
	{
	    if(!independent) render_front(ctx);

	    // render specific for widget
	    render_label(ctx);

	    float xgrid[ROWC_MAX_GRID], ygrid[ROWC_MAX_GRID], blend;
	    render_precalculate_grid(ctx, xgrid, ygrid, &blend);
	    render_grid(xgrid, ygrid, blend);
	    render_children(ctx, xgrid, ygrid, blend);
	}
	else if(ctx->get_pass()==RENDER_PASS_SELECT)
	{
	    render_front(ctx);

	    float xgrid[ROWC_MAX_GRID], ygrid[ROWC_MAX_GRID], blend;
	    render_precalculate_grid(ctx, xgrid, ygrid, &blend);
	    render_children(ctx, xgrid, ygrid, blend);
	}
    }
    render_base_end(ctx);
    render_finish(ctx);
}

void render_obj_widget_container::render_children(render_mng *ctx, float *xgrid, float *ygrid, float children_blend)
{
    float original_front_blend=root->get_front_blend();
    root->set_front_blend(children_blend);

    int x=0, y=0;
    for(int i=0; i<n_ro; i++)
    {
	float dims[IS_2D]={xgrid[x+1]-xgrid[x], ygrid[y+1]-ygrid[y]};
	if(dims[IS_X]>0 && dims[IS_Y]>0)
	{
	    render_obj_widget *child=dynamic_cast<render_obj_widget*>(ro[i]);

	    // translate child matrix
	    glTranslatef((xgrid[x]+xgrid[x+1])*0.5, (ygrid[y]+ygrid[y+1])*0.5, 0);
	    //child->get_modelview()->set_coef(0, 3, (xgrid[x]+xgrid[x+1])*0.5);
	    //child->get_modelview()->set_coef(1, 3, (ygrid[y]+ygrid[y+1])*0.5);

	    // scale child matrix via dims
	    child->set_dim(IS_X, dims[IS_X]);
	    child->set_dim(IS_Y, dims[IS_Y]);

	    // cursor position
	    //!!! child->cursor_position[IS_X]=0;

	    // render child
	    child->render(ctx);
	}

	// next cell
	if(++x>=grid_layout[IS_X]) {x=0; y++;}
    }

    root->set_front_blend(original_front_blend);
}

bool render_obj_widget_container::event(render_mng *ctx)
{
    render_obj_widget::event(ctx);
    if(!ctx->get_event_last())
    {
	event_mng *e=ctx->get_event();
	if(e->get_event_last()==OGL_MOUSE_MOVE_EVENT && independent) // rest of cursor positions are set in render-time
	{
	    double v[IS_3D];
	    ctx->get_selection_touch_point(v);
	    cursor_position[IS_X]=(v[IS_X]+dims[IS_X]*0.5)/dims[IS_X];
	    cursor_position[IS_Y]=(v[IS_Y]+dims[IS_Y]*0.5)/dims[IS_Y];
	    ctx->set_forced_pass(RENDER_PASS_OPAQUE, this);
	}
    }
    return false;
}

unsigned render_obj_widget_container::get_type()
{
    return IS_TYPE_WIDGET_CONTA;
}

void render_obj_widget_container::render_grid(float *xgrid, float *ygrid, float children_blend)
{
    // set blending
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    float c[IS_RGBA]={0.2, 0.2, 0.9, children_blend};
    glColor4fv(c);

    // actually draw
    float last=-1;
    glBegin(GL_LINES);
    for(int i=0; i<=grid_layout[IS_X]; i++)
    {
	if(last!=xgrid[i])
	{
	    glVertex2d(xgrid[i], ygrid[0]);
	    glVertex2d(xgrid[i], ygrid[grid_layout[IS_Y]]);
	    last=xgrid[i];
	}
    }
    for(int i=0; i<=grid_layout[IS_Y]; i++)
    {
	if(last!=ygrid[i])
	{
	    glVertex2d(xgrid[0], ygrid[i]);
	    glVertex2d(xgrid[grid_layout[IS_X]], ygrid[i]);
	    last=ygrid[i];
	}
    }
    glEnd();
}

void render_obj_widget_container::render_label(render_mng *ctx)
{
    // before depth_init
    float depth=root->get_depth()-depth_init;
    if(depth<depth_pre)
    {
	// just render a text
	ogl_font *f;
	if((f=ctx->get_font(RENDER_FONT_FT_DEFAULT)))
	{
	    float c[IS_RGBA]={0.2, 0.2, 0.9, root->get_front_blend()*(1-depth/depth_pre)};
	    glColor4fv(c);

	    float fdims[IS_3D];
	    f->size(fdims, "Container");                   // fdims[IS_X] contains X size in font units / pixels
	    float ratio=dims[IS_X]/fdims[IS_X];            // ratio to fit widget width
	    float ratio2=dims[IS_Y]/fdims[IS_Y];           // ratio to fit widget height
	    float ratio3=root->get_dims_to_pixel_ratio();  // ratio to scale font 1:1 in screen
	    if(ratio2<ratio) ratio=ratio2;
	    if(ratio3<ratio) ratio=ratio3;

	    glPushMatrix();
	    glTranslatef(-fdims[IS_X]*0.5*ratio, -fdims[IS_Y]*0.5*ratio, 0);
	    glScalef(ratio, ratio, ratio);
	    f->write("Container");
	    glPopMatrix();
	}
    }
}

void render_obj_widget_container::render_precalculate_grid(render_mng *ctx, float *xgrid, float *ygrid, float *children_blend)
{
    float depth=root->get_depth()-depth_init;

    // blend for grid and children
    *children_blend=root->get_front_blend()*((depth>depth_pre)?1:depth/depth_pre);

    depth-=depth_pre;
    float zoom=depth/(depth_zoom+depth_pre);
    if(zoom<0) zoom=0; //else if(zoom>1.0) zoom=1.0;

    float linear_zoomx=1+zoom*(grid_layout[IS_X]-1);
    float linear_zoomy=1+zoom*(grid_layout[IS_Y]-1);
    if(zoom_last>=0)
    {
	// update cursor position bias
	float linear_zoomx_old=1+zoom_last*(grid_layout[IS_X]-1);
	float linear_zoomy_old=1+zoom_last*(grid_layout[IS_Y]-1);

	float centerx=cursor_position[IS_X]/linear_zoomx_old-cursor_position_bias[IS_X];
	float centery=cursor_position[IS_Y]/linear_zoomy_old-cursor_position_bias[IS_Y];

	cursor_position_bias[IS_X]=(centerx*(linear_zoomx_old-linear_zoomx)+cursor_position_bias[IS_X]*linear_zoomx_old)/linear_zoomx;
	cursor_position_bias[IS_Y]=(centery*(linear_zoomy_old-linear_zoomy)+cursor_position_bias[IS_Y]*linear_zoomy_old)/linear_zoomy;
    }
    zoom_last=zoom;

    // save grid values
    for(int i=0; i<=grid_layout[IS_X]; i++) xgrid[i]=get_grid_x(i, linear_zoomx);
    for(int i=0; i<=grid_layout[IS_Y]; i++) ygrid[i]=get_grid_y(i, linear_zoomy);

#if 1
    printf("%p: ZOOM: %8.4f  X: %8.4f  Y: %8.4f   BIASX: %8.4f   BIASY: %8.4f  %8.4f\n", this, zoom,
	  cursor_position[IS_X], cursor_position[IS_Y],
	  cursor_position_bias[IS_X], cursor_position_bias[IS_Y], linear_zoomx);
#endif
}

float render_obj_widget_container::get_grid_x(int i, float linear_zoom)
{
    float x=(float)i/(float)grid_layout[IS_X];

    x=(x+cursor_position_bias[IS_X])*linear_zoom;
    if(x<0) x=0; else if(x>1) x=1;

    x-=0.5;
    x*=dims[IS_X];
    return x;
}

float render_obj_widget_container::get_grid_y(int i, float linear_zoom)
{
    float x=(float)i/(float)grid_layout[IS_Y];

    x=(x+cursor_position_bias[IS_Y])*linear_zoom;
    if(x<0) x=0; else if(x>1) x=1;

    x-=0.5;
    x*=dims[IS_Y];
    return x;
}
